#pragma once
/*
 *  SBJ/NXT/Command.h
 *  NXTFace
 *
 *  Created by David Giovannini on 8/23/06.
 *  Copyright 2006 Software by Jove. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */



#include "SBJ/NXT/Device.h"
#include "SBJ/NXT/CommandStatus.h"

#include <string>

namespace SBJ
{

namespace NXT
{

template < typename Traits >
class Command
{
public:

	typedef typename Traits::Params Params;
	typedef typename Traits::Results Results;
	
	typedef char ParamBuf[ sizeof( Params ) + 1 ];
	typedef char ResultBuf[ sizeof( Results ) + 2 ];
	
	Command( Device& device, const Params& params, Synchonous syncType = ( sizeof( Results ) == 0 ? async : sync ) )
	: m_syncType( syncType )
	{
		m_params[ 0 ] = Traits::code;
		::memcpy( m_params + ( sizeof( ParamBuf ) - sizeof( Params ) ), (const char*)&params, sizeof( Params ) );
		::memset( m_results, 0, sizeof( m_results ) );
		if ( m_syncType == sync )
			m_error = device.sendCommand( m_params, sizeof( m_params ), m_results, sizeof( m_results ) );
		else
			m_error = device.sendCommand( m_params, sizeof( m_params ), 0, 0 );
	}
	
	Command( Device& device, Synchonous syncType = ( sizeof( Results ) == 0 ? async : sync ) )
	: m_syncType( syncType )
	{
		if ( sizeof( Params ) != 0 ) throw 0; //std::exception( "" );
		m_params[ 0 ] = Traits::code;
		::memset( m_results, 0, sizeof( m_results ) );
		if ( m_syncType == sync )
			m_error = device.sendCommand( m_params, sizeof( m_params ), m_results, sizeof( m_results ) );
		else
			m_error = device.sendCommand( m_params, sizeof( m_params ), 0, 0 );
	}
	
	Synchonous synchonous( void ) const
	{
		return m_syncType;
	}
	
	Error error( void ) const
	{
		return m_error;
	}
	
	CommandStatus status( void ) const
	{
		if ( m_syncType == async ) throw 0;//std::exception( "" );
		return m_results[ 1 ];
	}
	
	const Results& result( void ) const
	{
		if ( sizeof( Results ) == 0 || m_syncType == async ) throw 0;//std::exception( "" );
		return *(Results*)( m_results + ( sizeof( ResultBuf ) - sizeof( Results ) ) );
	}
	
	const ParamBuf& paramBuf( void ) const
	{
		return m_params;
	}
	
	const ResultBuf& resultBuf( void ) const
	{
		return m_results;
	}
		
private:
	Error m_error;
	Synchonous m_syncType;
	ParamBuf m_params;
	ResultBuf m_results;
};

template < typename Cmd >
void print( const Cmd& cmd )
{
	::printf( "Params %d: ", sizeof( typename Cmd::ParamBuf ) );
	int i = 0;
	for ( ; i < sizeof( typename Cmd::ParamBuf ) - 1; ++i )
	{
		::printf( "%hh.2x.", cmd.paramBuf()[ i ] );
	}
	::printf( "%hh.2x\n", cmd.paramBuf()[ i ] );
	
	if ( cmd.synchonous() == sync )
	{
		::printf( "Results %d: ", sizeof( typename Cmd::ResultBuf ) );
		i = 0;
		for ( ; i < sizeof( typename Cmd::ResultBuf ) - 1; ++i )
		{
			::printf( "%hh.2x.", cmd.resultBuf()[ i ] );
		}
		::printf( "%hh.2x\n", cmd.resultBuf()[ i ] );
		if ( cmd.status() != CommandStatus::good )
		{
			::printf( "Status: %d:%s\n", (int)cmd.status(), cmd.status().message().c_str() );
		}
	}
	else
	{
		::printf( "Results: async\n" );
	}
	::printf( "Error: %d:%s\n\n", (int)cmd.error(), cmd.error().message().c_str() );
}

}

}